home *** CD-ROM | disk | FTP | other *** search
/ Linux Programs 1995 Summer / Linux Programs.iso / c_devel / clone_gl.c < prev    next >
C/C++ Source or Header  |  1995-04-15  |  7KB  |  212 lines

  1. From db@argon.Eng.Sun.COM Mon Jan  2 15:43:49 EST 1995
  2. Article: 24101 of comp.os.linux.development
  3. Path: bigblue.oit.unc.edu!concert!gatech!swrinde!elroy.jpl.nasa.gov!decwrl!koriel!male.EBay.Sun.COM!engnews2.Eng.Sun.COM!argon!db
  4. From: db@argon.Eng.Sun.COM (David Brownell)
  5. Newsgroups: comp.os.linux.development
  6. Subject: Re: Q: multi-thread?
  7. Date: 2 Jan 1995 18:33:32 GMT
  8. Organization: SunSoft, Inc.
  9. Lines: 196
  10. Message-ID: <3e9gts$qqe@engnews2.Eng.Sun.COM>
  11. References: <3dsm50$9m3@engr.orst.edu> <3e1uho$5ml@fido.asd.sgi.com> <3e2dfd$ove@senator-bedfellow.mit.edu> <smcneilD1rM7y.G31@netcom.com>
  12. NNTP-Posting-Host: argon.eng.sun.com
  13.  
  14. I spent a few hours hacking away to see what this clone() syscall would do.
  15. Here's a simple test program, with comments that should compensate for the
  16. lack of documentation on the kernel call.
  17.  
  18. In its current incarnation, clone() can't be used as the basis of a full
  19. fidelity POSIX threading implementation.  However, it may be a reasonable
  20. base on which to ** start ** making the LINUX libraries behave correctly
  21. in the face of multiple threads; stuff like errno and stdio for example.
  22.  
  23. Is there a project under way to make the LINUX libraries MT-safe?
  24.  
  25. - Dave
  26.  
  27.  
  28. /*
  29.  * Reverse-engineered C glue for access to LINUX "clone" system call.  This
  30.  * is basically fork() that shares a bit more data (but not enough to build
  31.  * a complete POSIX threads implementation).
  32.  *
  33.  * Note that clone() is experimental and incomplete.  This file is being
  34.  * constructed to help explore how to implement POSIX threads.  USE AT YOUR
  35.  * OWN RISK, no warrantees expressed or implied, etc.  Matches Fall '94
  36.  * Yggdrasil LINUX (1.1.47+ kernel, see fork.c), your mileage may vary.
  37.  *
  38.  * KERNEL parameters are as follows (and don't match the call signature
  39.  * used outside the kernel, for reasons that will be apparent):
  40.  *
  41.  * - "clone_esp" ... if set to the parent's ESP, causes COPYVM to be set.
  42.  *   In any case, this is the new process's ESP; caller must establish a
  43.  *   valid stack frame there for the clone to return to.
  44.  *
  45.  * - "flags" ... zero or more values logically "or"ed together:
  46.  *    COPYVM ... copies address space (only on write :-).  If not set,
  47.  *        shares the entire address space, but changes made to the
  48.  *        address space itself (e.g. shmat, munmap) are not shared.
  49.  *
  50.  *    COPYFD ... copies all open file descriptors.  If not set, shares
  51.  *        individual table entries, but won't pick up changes to the
  52.  *        file descriptor table itself (e.g. open, close).
  53.  *
  54.  *    "SIGNAL" ... the number of a signal that will be sent to the parent
  55.  *        when the cloned process dies.  If set to zero, wait4() must
  56.  *        pass __WCLONE to wait for clones.
  57.  *
  58.  * The "CLONEVM" code (COPYVM clear) is noted as incomplete/wrong.  Races may
  59.  * exist, and it doesn't treat SysV SHM the way it treats other memory.
  60.  *
  61.  * For the purpose of using clone() to implement POSIX threads, clones need
  62.  * to share the entire address space so that modifications (mprotect, sbrk,
  63.  * mmap, shmat, ...) made by one clone (or parent) are seen by all others
  64.  * immediately.  Otherwise, functions and data won't be uniformly accessible
  65.  * to all threads.
  66.  *
  67.  *
  68.  * Similarly, the "SHAREFD" code (COPYFD clear) seems a bit odd ... nobody
  69.  * uses COPYFD (that I could easily find), and it doesn't quite seem to do
  70.  * what was originally intended.
  71.  *
  72.  * For the purpose of implementing POSIX threads, the clones and parents need
  73.  * to share the file table so that changes made by one are seen by the others.
  74.  * Neither "CLONEFD" nor "SHAREFD" has the right semantics.
  75.  *
  76.  *
  77.  * NOTE:  There's all kinds of process state that really ought to be shared
  78.  * between "clones" in order to support POSIX threads.  Without looking at
  79.  * POSIX specs (just some LINUX source) this would seem to include at least:
  80.  *
  81.  *    - address space (as noted above)
  82.  *    - file descriptors (as noted above)
  83.  *    - cwd and root inodes
  84.  *    - signal handling
  85.  *    - control TTY
  86.  *    - SysV IPC (SHM, SEM, MSGQ)
  87.  *    - real and effective UIDs
  88.  *    - ... and more
  89.  *
  90.  * It's this required degree of sharing that led at least one OS to make
  91.  * the kernel-supported thread context be distinct from processes; the
  92.  * threads share all that stuff via the proceses to which they're tied.
  93.  * The kernel thread context is what's involved in scheduling decisions,
  94.  * and POSIX threading uses a "create kernel thread" style primitive that
  95.  * shares all the process context, not a fork() variant that's got to be
  96.  * taught on an item-by-item basis what needs to be shared.
  97.  *
  98.  * SUGGESTION:  There should be a new kernel data structure representing
  99.  * the kernel portion of a POSIX thread.  This includes fields from the
  100.  * LINUX "task_struct" (in <linux/sched.h>) and would have fields like
  101.  *
  102.  *    - tss (for per-thread context, from task_struct)
  103.  *    - kernel stack page (from task_struct)
  104.  *    - back pointer to task_struct (the 'process')
  105.  *
  106.  * One such structure could be preallocated in each "task_struct"; it'd be
  107.  * useful for smooth kernel evolution if nothing else.  Threaded processes
  108.  * would have a set of such structures; unthreaded ones, just the single one.
  109.  * Scheduling would need to work in terms of those structures rather than
  110.  * today's "task_struct".  All accesses (!) to today's "task_struct" would
  111.  * need to indirect to it; "current" might be a macro (curthread->process).
  112.  */
  113.  
  114. #include    <syscall.h>
  115. #include    <unistd.h>
  116.  
  117. #include    <sys/types.h>
  118.  
  119. /*
  120.  * XXX this calling convention is bogus -- no MT-oriented calls
  121.  * should use global state such as errno.
  122.  */
  123. pid_t
  124. clone (
  125.     void    (*func)(void *ctx),    /* function for clone to call */
  126.     void    *ctx,            /* parameter to call it with */
  127.     void    *stack,            /* stack to use */
  128.     size_t    stack_len,        /* size thereof, in bytes */
  129.     int        lwp_flags        /* flags (UNUSED) */
  130. )
  131. {
  132.     void    **esp = (void **) ((((long)stack) + stack_len) & ~0x03);
  133.     pid_t    retval;
  134.  
  135.     /*
  136.      * Make a stack that unwinds nicely enough ... syscall returns
  137.      * to the "func" that the clone is to call, with its parameter
  138.      * in normal position.  If the clone returns, it exits.
  139.      */
  140.     *--esp = 0;                /* parameter to _exit */
  141.     *--esp = ctx;            /* parameter to func */
  142.     *--esp = (void *) _exit;        /* return address for "func" */
  143.  
  144.     *--esp = func;            /* "return" address for clone */
  145.     *--esp = 0;                /* params popped by syscall */
  146.     *--esp = 0;
  147.     *--esp = 0;
  148.  
  149.     return syscall (SYS_clone, esp, /* clone_flags */ 0, 0, 0, 0);
  150. }
  151.  
  152.  
  153. /*
  154.  * Some sample code to test _clone() ...
  155.  */
  156.  
  157. #include    <signal.h>
  158. #include    <stdio.h>
  159. #include    <string.h>
  160.  
  161. #include    <linux/sched.h>
  162.  
  163. static void
  164. clone_call (void *param)
  165. {
  166.     char *str = (char *)param;
  167.  
  168.     /*
  169.      * RACE -- runtime libraries are not yet reentrant
  170.      * So we emit our message and exit without cleanup.
  171.      */
  172.     printf ("%s, I'm clone %d!\n", str, getpid ());
  173.     fflush (stdout);
  174. }
  175.  
  176. static char    new_stack [4096];
  177.  
  178. int
  179. main ()
  180. {
  181.     pid_t    clone_pid;
  182.  
  183.     clone_pid = clone (clone_call, "hello world",
  184.             new_stack, sizeof new_stack,
  185.             0);
  186.  
  187.     switch (clone_pid) {
  188.       case -1:
  189.     perror ("clone");
  190.     return -1;
  191.  
  192.       case 0:
  193.         puts ("?? Parent is the clone ??");
  194.         return -1;
  195.  
  196.       default:
  197.     /*
  198.      * Runtime libraries are not yet reentrant, we've got
  199.      * problems doing the most basic stuff, like having
  200.      * both clones use STDIO.
  201.      */
  202.     _exit (0);
  203.     }
  204. }
  205. -- 
  206. David Brownell                        db@Eng.Sun.COM.
  207. Distributed Object Management
  208.  
  209. main(a){printf(a,34,a="main(a){printf(a,34,a=%c%s%c,34);}",34);}
  210.  
  211.  
  212.